package comparisons

import (
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
)

// 弹出栈顶两个long类型的数值并进行比较。根据比较结果的不同，将-1 或0 或1压入操作数栈
// 如果value1 > value2 压入1
// 如果value1 = value2 压入0
// 如果value1 < value2 压入-1
type LCMP struct {
	base.NoOperandsInstruction
}

func (self *LCMP) Execute(frame *runtime_area.Frame) {
	stack := frame.OperandStack()
	// 例如java代码 a > b，操作数入栈时是a先入栈，b后入栈。
	// 然后到这个比较指令进行比较时，是b先出栈，a后出栈，下面的next变量获取到的值是b，pre获取到的值是a
	next := stack.PopLong()
	pre := stack.PopLong()
	if pre > next {
		stack.PushInt(1)
	} else if pre < next {
		stack.PushInt(-1)
	} else if pre == next {
		stack.PushInt(0)
	}

}

// 比较栈顶两个float数值的大小，当其中一个值为NAN时，将1压入栈顶
type FCMPG struct {
	base.NoOperandsInstruction
}

// 比较栈顶两个float数值的大小，当其中一个值为NAN时，将-1压入栈顶
type FCMPL struct {
	base.NoOperandsInstruction
}

func (self *FCMPG) Execute(frame *runtime_area.Frame) {
	_fcmp(frame, 1)
}

func (self *FCMPL) Execute(frame *runtime_area.Frame) {
	_fcmp(frame, -1)
}

func _fcmp(frame *runtime_area.Frame, valueOnNaN int32) {
	stack := frame.OperandStack()
	next := stack.PopFloat()
	pre := stack.PopFloat()
	if pre > next {
		stack.PushInt(1)
	} else if pre < next {
		stack.PushInt(-1)
	} else if pre == next {
		stack.PushInt(0)
	} else {
		// 两个float，不是大于小于等于中的任何一个关系，那么一定有其中一个数为NaN
		stack.PushInt(valueOnNaN)
	}
}

// 比较栈顶两个double数值的大小，当其中一个值为NAN时，将-1压入栈顶
type DCMPL struct {
	base.NoOperandsInstruction
}

// 比较栈顶两个double数值的大小，当其中一个值为NAN时，将1压入栈顶
type DCMPG struct {
	base.NoOperandsInstruction
}

func (self *DCMPG) Execute(frame *runtime_area.Frame) {
	_fcmp(frame, 1)
}


func (self *DCMPL) Execute(frame *runtime_area.Frame) {
	_dcmp(frame, -1)
}

func _dcmp(frame *runtime_area.Frame, valueOnNaN int32) {
	stack := frame.OperandStack()
	next := stack.PopDouble()
	pre := stack.PopDouble()
	if pre > next {
		stack.PushInt(1)
	} else if pre < next {
		stack.PushInt(-1)
	} else if pre == next {
		stack.PushInt(0)
	} else {
		// 两个double，不是大于小于等于中的任何一个关系，那么一定有其中一个数为NaN
		stack.PushInt(valueOnNaN)
	}
}
